package mips.instructions;

/**
 * <code>DIVU</code> instruction<br/>
 * Divide Unsigned<br/>
 * @author jnmartin84@gmail.com
 */
public class DIVU extends Instruction {

	private static final DIVU INSTANCE = new DIVU();
	private static final String INSTRUCTION_NAME = "DIVU";

	private DIVU(){}

	public static final DIVU getInstance() {
		return INSTANCE;
	}

	/**
	 * <b>Format:</b><br/>
	 * DIVU rs, rt<br/><br/>
	 * <b>Description:</b><br/>
	 * The contents of general register rs are divided by the contents of general<br/>
	 * register rt, treating both operands as unsigned values. No integer<br/>
	 * overflow exception occurs under any circumstances, and the result of this<br/>
	 * operation is undefined when the divisor is zero.<br/><br/>
	 * This instruction is typically followed by additional instructions to check<br/>
	 * for a zero divisor.<br/><br/>
	 * When the operation completes, the quotient word of the double result is<br/>
	 * loaded into special register LO, and the remainder word of the double<br/>
	 * result is loaded into special register HI.<br/><br/>
	 * If either of the two preceding instructions is MFHI or MFLO, the results of<br/>
	 * those instructions are undefined. Correct operation requires separating<br/>
	 * reads of HI or LO from writes by two or more instructions.<br/><br/>
	 * <b>Operation:</b><br/>
	 * T-2: LO &larr; undefined<br/>
	 * HI &larr; undefined<br/>
	 * T-1: LO &larr; undefined<br/>
	 * HI &larr; undefined<br/>
	 * T: LO &larr; (0 || GPR[rs]) div (0 || GPR[rt])<br/>
	 * HI &larr; (0 || GPR[rs]) mod (0 || GPR[rt])[rt] 
	 */
	@Override
	public final void execute(final int instruction) {

		mips.instructions.Instruction.RS = (instruction >> 21) & 0x0000001F;
		mips.instructions.Instruction.RT = (instruction >> 16) & 0x0000001F;

		final long u32_gpr_rs = mips.instructions.Instruction.zeroExtendW(mips.R4300i.GPR[mips.instructions.Instruction.RS]);
		final long u32_gpr_rt = mips.instructions.Instruction.zeroExtendW(mips.R4300i.GPR[mips.instructions.Instruction.RT]);

		mips.R4300i.LO = (int)((u32_gpr_rs / u32_gpr_rt) & mips.Constants.MAX_U32_AS_S64);
		mips.R4300i.HI = (int)((u32_gpr_rs % u32_gpr_rt) & mips.Constants.MAX_U32_AS_S64);

		mips.R4300i.PC = mips.R4300i.nPC;
		mips.R4300i.nPC = mips.R4300i.PC + 4;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public final String emit(int instruction) {
		
		mips.instructions.Instruction.RS = (instruction >> 21) & 0x0000001F;
		mips.instructions.Instruction.RT = (instruction >> 16) & 0x0000001F;
		
		return	"		u32_gpr_rs = mips.instructions.Instruction.zeroExtendW(mips.CPU.GPR["+mips.instructions.Instruction.RS+"]);\n" + 
				"		u32_gpr_rt = mips.instructions.Instruction.zeroExtendW(mips.CPU.GPR["+mips.instructions.Instruction.RT+"]);\n" + 
				"		\n" + 
				"		mips.CPU.LO = (int)((u32_gpr_rs / u32_gpr_rt) & "+mips.Constants.MAX_U32_AS_S64+"L);\n" + 
				"		mips.CPU.HI = (int)((u32_gpr_rs % u32_gpr_rt) & "+mips.Constants.MAX_U32_AS_S64+"L);\n" + 
				"		\n" + 
				"		mips.CPU.PC = mips.CPU.nPC;\n" + 
				"		mips.CPU.nPC = mips.CPU.PC + 4;\n";
	}
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public final String getName(final int instruction) {
		return getName();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public final String getName() {
		return INSTRUCTION_NAME;
	}
}